import * as net from 'net';
import {ProtocolDecode} from "./protocol-decode.mjs";
import {ProtocolType} from "./protocol-type.mjs";
import {RSAUtil} from "./rsa-util.mjs";
import {SimpleProtocol} from "./simple-protocol.mjs";
import {ProtocolStatus} from "./protocol-status.mjs";
import {AesUtil} from "./aes-util.mjs";

export class SimpleSocketClient {
    #decoder = new ProtocolDecode();
    sessionID;
    privateRSAKey
    #aes
    #serverPubRSA;

    #readyCallback

    constructor() {
        this.client = new net.Socket();
        this.client.on('data',this.#dataReceived.bind(this));
        this.router = new Map();
    }

    connect(){
        this.client.connect(8080,'127.0.0.1')
    }

    onConnected(callback){
        this.#readyCallback = callback;
    }

    subscribe(uri,callback){
        this.router.set(uri,callback);
    }

    unsubscribe(uri){
        this.router.delete(uri)
    }

    send(uri,data,callback){
        const encryptedData = AesUtil.encrypt(JSON.stringify(data),this.#aes);
        const sp = new SimpleProtocol(ProtocolType.NORMAL,
            ProtocolStatus.NORMAL,this.sessionID,uri,encryptedData);
        this.subscribe(uri,callback);
        this.#send(sp);
    }

    #dataReceived(data){
        try {
            this.#decoder.append(data);

            let protocol;
            while ((protocol = this.#decoder.parse()) !== null) {
                this.#distributeRoute(protocol).then(() => {})
            }
        } catch (error) {
            if (error.message === 'Invalid magic number' || error.message === 'Invalid protocol version') {
                console.error(error.message);
                this.client.destroy(); // 关闭连接
                return;
            }
            console.error('Error parsing data:', error);
        }
    }

    #listenSessionID(data){
        this.sessionID = data.sessionID
        this.#getKeys();
    }

    #getKeys(){
        const keys = RSAUtil.generateKeyStrings();
        const pubKey = keys[0];
        this.privateRSAKey = keys[1];
        this.#send(new SimpleProtocol(ProtocolType.DISCUSS_KEYS,ProtocolStatus.NORMAL,
            this.sessionID,'',pubKey))
    }

    #listenKeys(data){
        this.#aes = RSAUtil.decrypt(this.privateRSAKey,data.aes);
        this.#serverPubRSA = data.serverPubRSA;
        this.#readyCallback();
        console.log("连接建立完毕");
    }

    #listenReconnect(data){

    }

    async #distributeRoute(protocol){
        if (protocol.type === ProtocolType.CONNECTED){
            this.#listenSessionID(JSON.parse(protocol.data))
        }else if (protocol.type === ProtocolType.DISCUSS_KEYS){
            this.#listenKeys(JSON.parse(protocol.data))
        }else if (protocol.type === ProtocolType.RECONNECT){
            this.#listenReconnect(JSON.parse(protocol.data))
        }else if (protocol.type === ProtocolType.NORMAL){
            const func = this.router.get(protocol.uri);
            console.log("得到的加密数据是：" + protocol.data)
            try {
                func(JSON.parse(AesUtil.decrypt(protocol.data,this.#aes)))
            }catch (e){
                func(AesUtil.decrypt(protocol.data,this.#aes));
            }
        }else {
            console.error('No such type in protocol')
        }
    }

    #send(protocol){
        const dataBuf = Buffer.from(protocol.data);

        const contentLength = 4 + 1 + 1 + 32 + 4 + protocol.uri.length + protocol.data.length;

        const lengthBuf = Buffer.alloc(4);
        lengthBuf.writeInt32BE(contentLength);

        const magicBuf = Buffer.alloc(4);
        magicBuf.writeInt32BE(SimpleProtocol.MAGIC_NUMBER);

        const typeBuf = Buffer.alloc(1);
        typeBuf.writeInt8(protocol.type);

        const statusBuf = Buffer.alloc(1);
        statusBuf.writeInt8(ProtocolStatus.NORMAL);

        const sessionIDBuf = Buffer.alloc(32);
        sessionIDBuf.write(this.sessionID,'utf-8');

        const uriLengthBuf = Buffer.alloc(4);
        uriLengthBuf.writeInt32BE(protocol.uri.length);

        const uriBuf = Buffer.from(protocol.uri);

        const finalBuffer = Buffer.concat([lengthBuf, magicBuf,typeBuf,statusBuf,
            sessionIDBuf,uriLengthBuf,uriBuf, dataBuf]);
        this.client.write(finalBuffer)
    }
}